package com.sqs.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sqs.constant.JwtClaimsConstant;
import com.sqs.constant.ExceptionConstant;
import com.sqs.constant.RedisConstant;
import com.sqs.constant.StatusConstant;
import com.sqs.context.BaseContext;
import com.sqs.dto.EditPasswordDTO;
import com.sqs.dto.UserEnrollDTO;
import com.sqs.dto.UserLoginDTO;
import com.sqs.dto.UserUpdateDTO;
import com.sqs.entity.User;
import com.sqs.exception.*;
import com.sqs.mapper.UserMapper;
import com.sqs.properties.JwtProperties;
import com.sqs.service.IUserService;
import com.sqs.utils.JwtUtil;
import com.sqs.vo.UserLoginVO;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 用户注册
     * @param userEnrollDTO
     * */
    public void insert(UserEnrollDTO userEnrollDTO) {
        //1.判断验证码是否正确
        checkCaptcha(userEnrollDTO.getCaptcha(), userEnrollDTO.getKey());
        //2.补充剩余字段
        User user = new User();
        BeanUtils.copyProperties(userEnrollDTO, user);
        user.setStatus(StatusConstant.ENABLE);
        //3.将密码加密
        user.setPassword(DigestUtils.md5DigestAsHex(user.getPassword().getBytes()));
        //4.存入数据库
        save(user);
    }

    /**
     * 用户登录
     * @param userLoginDTO
     * @return
     */
    public UserLoginVO login(UserLoginDTO userLoginDTO) {
        //1.判断验证码是否正确
        checkCaptcha(userLoginDTO.getCaptcha(), userLoginDTO.getKey());

        //2.查询是否有该用户
        User user = lambdaQuery().eq(User::getUsername, userLoginDTO.getUsername()).one();
        String password = DigestUtils.md5DigestAsHex(userLoginDTO.getPassword().getBytes());
        if(user == null){
            throw new BaseException(ExceptionConstant.CODE_404, ExceptionConstant.USER_NOT_EXIST);
        }
        //3.判断用户账号是否被冻结
        if(Objects.equals(user.getStatus(), StatusConstant.UNABLE)){
            throw new BaseException(ExceptionConstant.CODE_401, ExceptionConstant.USER_UNABLE);
        }
        //4.判断密码是否正确
        if(!password.equals(user.getPassword())){
            throw new BaseException(ExceptionConstant.CODE_401, ExceptionConstant.PASSWORD_ERROR);
        }
        //5.登录成功，生成jwt令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.USER_ID, user.getId());
        String token = JwtUtil.createJWT(jwtProperties.getUserSecretKey(), claims);
        //6.将token存入redis
        stringRedisTemplate.opsForValue().set(RedisConstant.TOKEN_HEAD + user.getId(), token, RedisConstant.TOKEN_TTL, TimeUnit.MINUTES);
        return UserLoginVO.builder()
                .id(user.getId())
                .token(token)
                .build();
    }

    /**
     * 判断验证码是否正确
     * @param captcha
     * @param key
     * */
    private void checkCaptcha(@NotNull(message = "验证码不能为空") String captcha,
                              String key){
        String captcha1 = stringRedisTemplate.opsForValue().get(RedisConstant.CAPTCHA_HEAD + key);
        if(!captcha1.equals(captcha.toLowerCase())){
            throw new BaseException(ExceptionConstant.CODE_400, ExceptionConstant.CAPTCHA_ERROR);
        }
    }

    /**
     * 更改用户启用禁用状态
     * @param id
     * @param status
     * */
    public void stopOrStart(Long id, Integer status) {
        User user = User.builder()
                .id(id)
                .status(status)
                .build();
        updateById(user);
    }

    /**
     * 根据id查询用户
     * @param id
     * @return
     * */
    public User getById(Long id) {
        User user = userMapper.selectById(id);
        if(user == null){
            throw new BaseException(ExceptionConstant.CODE_404, ExceptionConstant.USER_NOT_EXIST);
        }
        user.setPassword("******");
        return user;
    }

    /**
     * 更改用户密码
     * @param editPasswordDTO
     * */
    public void editPassword(EditPasswordDTO editPasswordDTO) {
        //1.根据id查询用户
        User user = userMapper.getById(editPasswordDTO.getId());
        //2.判断用户是否存在
        if(BeanUtil.isEmpty(user)){
            throw new BaseException(ExceptionConstant.CODE_404, ExceptionConstant.USER_NOT_EXIST);
        }
        String oldPassword = DigestUtils.md5DigestAsHex(editPasswordDTO.getOldPassword().getBytes());
        String newPassword = DigestUtils.md5DigestAsHex(editPasswordDTO.getNewPassword().getBytes());
        //3.判断旧密码是否正确
        if(oldPassword.equals(user.getPassword())){
            //如果密码正确就修改密码
            LambdaUpdateWrapper<User> userLambdaUpdateWrapper = Wrappers.lambdaUpdate(User.class)
                    .set(User::getPassword, newPassword)
                    .set(User::getUpdateTime, LocalDateTime.now())
                    .eq(User::getId, editPasswordDTO.getId());
            userMapper.update(userLambdaUpdateWrapper);
        }
        else{
            throw new BaseException(ExceptionConstant.CODE_401, ExceptionConstant.PASSWORD_ERROR);
        }
    }

    /**
     * 更新用户信息
     * @param userUpdateDTO
     * */
    public void update(UserUpdateDTO userUpdateDTO) {
        User user = User.builder()
                .id(userUpdateDTO.getId())
                .imageUrl(userUpdateDTO.getImageUrl())
                .build();
        updateById(user);
    }

    /**
     * 退出登录
     * */
    public void loginOut() {
        stringRedisTemplate.opsForValue().getAndDelete(RedisConstant.TOKEN_HEAD + BaseContext.getCurrentId());
    }


}
